﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Xms.Abp.Features;

public class FeatureChecker : FeatureCheckerBase
{
    protected FeatureOptions Options { get; }
    protected IServiceProvider ServiceProvider { get; }
    protected IFeaturesDefinitionManager FeatureDefinitionManager { get; }
    protected List<IFeaturesValueProvider> Providers => _providers.Value;

    private readonly Lazy<List<IFeaturesValueProvider>> _providers;

    public FeatureChecker(
        IOptions<FeatureOptions> options,
        IServiceProvider serviceProvider,
        IFeaturesDefinitionManager featureDefinitionManager)
    {
        ServiceProvider = serviceProvider;
        FeatureDefinitionManager = featureDefinitionManager;

        Options = options.Value;

        _providers = new Lazy<List<IFeaturesValueProvider>>(
            () => Options
                .ValueProviders
                .Select(type => ServiceProvider.GetRequiredService(type) as IFeaturesValueProvider)
                .ToList(),
            true
        );
    }

    public override async Task<string> GetOrNullAsync(string name)
    {
        var featureDefinition = FeatureDefinitionManager.Get(name);
        var providers = Enumerable
            .Reverse(Providers);

        if (featureDefinition.AllowedProviders.Any())
        {
            providers = providers.Where(p => featureDefinition.AllowedProviders.Contains(p.Name));
        }

        return await GetOrNullValueFromProvidersAsync(providers, featureDefinition);
    }

    protected virtual async Task<string> GetOrNullValueFromProvidersAsync(
        IEnumerable<IFeaturesValueProvider> providers,
        FeatureValueDefinition feature)
    {
        foreach (var provider in providers)
        {
            var value = await provider.GetOrNullAsync(feature);
            if (value != null)
            {
                return value;
            }
        }

        return null;
    }
}
